Refactor some more of the ugly b/c update logic out of updaters.inc
[lhc/web/wiklou.git] / includes / installer / Update.php
1 <?php
2 /*
3 * Class for handling database updates. Roughly based off of updaters.inc, with
4 * a few improvements :)
5 */
6
7 class Update {
8
9 // Array of updates to perform on the database
10 protected $updates = array();
11
12 // Things we'll need
13 protected $db, $updater;
14
15 public function __construct( $db ) {
16 $this->db = $db;
17 switch( $this->db->getType() ) {
18 case 'mysql':
19 $this->updater = new MysqlUpdater();
20 break;
21 case 'sqlite':
22 $this->updater = new SqliteUpdater();
23 break;
24 case 'oracle':
25 $this->updater = new OracleUpdater();
26 break;
27 default:
28 throw new MWException( __METHOD__ . ' called for unsupported $wgDBtype' );
29 }
30 }
31
32 public function doUpdates() {
33 global $IP;
34 require_once( "$IP/maintenance/updaters.inc" );
35 $this->loadUpdates();
36 foreach ( $this->updates as $version => $updates ) {
37 foreach( $updates as $params ) {
38 $func = array_shift( $params );
39 call_user_func_array( $func, $params );
40 flush();
41 }
42 // some updates don't get recorded :(
43 if( $version !== 'always' ) {
44 $this->setAppliedUpdates( $version, $updates );
45 }
46 }
47 }
48
49 protected function loadUpdates() {
50 // If the updatelog table hasn't been upgraded, we can't use the new
51 // style of recording our steps. Run all to be safe
52 if( !$this->canUseNewUpdatelog() ) {
53 $this->updates = $this->updater->getUpdates();
54 } else {
55 foreach( $this->updater->getUpdates() as $version => $updates ) {
56 $appliedUpdates = $this->getAppliedUpdates( $version );
57 if( !$appliedUpdates || $appliedUpdates != $updates ) {
58 $this->updates[ $version ] = $updates;
59 }
60 }
61 }
62 $this->getOldGlobalUpdates();
63 }
64
65 protected function getAppliedUpdates( $version ) {
66 $key = "updatelist-$version";
67 $val = $this->db->selectField( 'updatelog', 'ul_value',
68 array( 'ul_key' => $key ), __METHOD__ );
69 if( !$val ) {
70 return null;
71 } else {
72 return unserialize( $val );
73 }
74 }
75
76 protected function setAppliedUpdates( $version, $updates = array() ) {
77 if( !$this->canUseNewUpdatelog() ) {
78 return;
79 }
80 $key = "updatelist-$version";
81 $this->db->delete( 'updatelog', array( 'ul_key' => $key ), __METHOD__ );
82 $this->db->insert( 'updatelog',
83 array( 'ul_key' => $key, 'ul_value' => serialize( $updates ) ),
84 __METHOD__ );
85 }
86
87 /**
88 * Updatelog was changed in 1.17 to have a ul_value column so we can record
89 * more information about what kind of updates we've done (that's what this
90 * class does). Pre-1.17 wikis won't have this column, and really old wikis
91 * might not even have updatelog at all
92 *
93 * @return boolean
94 */
95 protected function canUseNewUpdatelog() {
96 return $this->db->tableExists( 'updatelog' ) &&
97 $this->db->fieldExists( 'updatelog', 'ul_value' );
98 }
99
100 /**
101 * Before 1.17, we used to handle updates via stuff like $wgUpdates,
102 * $wgExtNewTables/Fields/Indexes. This is nasty :) We refactored a lot
103 * of this in 1.17 but we want to remain back-compatible for awhile. So
104 * load up these old global-based things into our update list. We can't
105 * version these like we do with our core updates, so they have to go
106 * in 'always'
107 */
108 private function getOldGlobalUpdates() {
109 global $wgUpdates, $wgExtNewFields, $wgExtNewTables,
110 $wgExtModifiedFields, $wgExtNewIndexes;
111
112 if( isset( $wgUpdates[ $this->db->getType() ] ) ) {
113 foreach( $wgUpdates[ $this->db->getType() ] as $upd ) {
114 $this->updates['always'][] = $upd;
115 }
116 }
117
118 foreach ( $wgExtNewTables as $tableRecord ) {
119 $this->updates['always'][] = array(
120 'add_table', $tableRecord[0], $tableRecord[1], true
121 );
122 }
123
124 foreach ( $wgExtNewFields as $fieldRecord ) {
125 if ( $fieldRecord[0] != 'user' || $doUser ) {
126 $this->updates['always'][] = array(
127 'add_field', $fieldRecord[0], $fieldRecord[1],
128 $fieldRecord[2], true
129 );
130 }
131 }
132
133 foreach ( $wgExtNewIndexes as $fieldRecord ) {
134 $this->updates['always'][] = array(
135 'add_index', $fieldRecord[0], $fieldRecord[1],
136 $fieldRecord[2], true
137 );
138 }
139
140 foreach ( $wgExtModifiedFields as $fieldRecord ) {
141 $this->updates['always'][] = array(
142 'modify_field', $fieldRecord[0], $fieldRecord[1],
143 $fieldRecord[2], true
144 );
145 }
146 }
147 }